/**
 * 事件发射器类，实现了观察者模式的事件订阅与发布机制
 * 
 * 支持两种类型的事件监听器：
 * - 无参数监听器：() => void
 * - 带参数监听器：(detail: T) => void
 * 
 * 事件系统遵循以下原则：
 * 1. 同一事件可以注册多个监听器
 * 2. 事件触发时会按注册顺序依次调用所有监听器
 * 3. 支持监听器的精确移除
 * 4. 事件触发时的异常不会中断其他监听器执行
 * 
 * @example
 * ```typescript
 * const emitter = new EventEmitter();
 * 
 * // 注册带参数事件
 * emitter.on<number>('progress', (percent) => {
 *   console.log(`进度: ${percent}%`);
 * });
 * 
 * // 注册无参数事件
 * emitter.on('complete', () => {
 *   console.log('完成');
 * });
 * 
 * // 触发事件
 * emitter.emit<number>('progress', 50);
 * emitter.emit('complete');
 * ```
 * @module EventEmitter
 */
export class EventEmitter {
    /**
     * 存储事件名称与监听器数组的映射关系
     * @private
     * @type {Record<string, Array<(() => void) | ((detail: any) => void)>>}
     * @description 键为事件名称，值为对应事件的监听器函数数组，支持无参数和带参数两种监听器类型
     */
    private __events: Record<string, Array<(() => void) | ((detail: any) => void)>>;

    /**
     * 创建事件发射器实例
     * @constructor
     * @description 初始化事件存储对象，采用Object.create(null)创建无原型链的纯净对象
     */
    constructor() {
        this.__events = Object.create(null);
    }

    /**
     * 注册无参数事件监听器
     * @param {string} eventName - 事件名称
     * @param {() => void} listener - 无参数的事件处理函数
     * @example
     * emitter.on('ready', () => console.log('准备就绪'));
     */
    public on(eventName: string, listener: () => void): void;
    
    /**
     * 注册带参数事件监听器
     * @template T - 事件参数的类型
     * @param {string} eventName - 事件名称
     * @param {(detail: T) => void} listener - 带参数的事件处理函数
     * @example
     * emitter.on<{id: number, name: string}>('user', (user) => {
     *   console.log(`用户: ${user.name}`);
     * });
     */
    public on<T = any>(eventName: string, listener: (detail: T) => void): void;
    
    /**
     * 事件监听实现
     * @param {string} eventName - 事件名称
     * @param {(() => void) | ((detail: any) => void)} listener - 事件处理函数
     * @description 将监听器添加到指定事件的监听器数组中，如果事件不存在则创建
     */
    public on(eventName: string, listener: (() => void) | ((detail: any) => void)): void {
        if (!this.__events[eventName]) {
            this.__events[eventName] = [];
        }
        this.__events[eventName].push(listener);
    }

    /**
     * 移除指定事件的监听器
     * @param {string} eventName - 事件名称
     * @param {Function} listener - 要移除的监听器函数
     * @description 从事件的监听器数组中移除指定的监听器，使用严格相等(===)进行匹配
     * @example
     * const handler = () => console.log('处理');
     * emitter.on('event', handler);
     * emitter.off('event', handler);
     */
    public off(eventName: string, listener: Function): void {
        if (this.__events[eventName]) {
            this.__events[eventName] = this.__events[eventName].filter(l => l !== listener);
        }
    }

    /**
     * 触发无参数事件
     * @param {string} eventName - 事件名称
     * @description 调用该事件的所有无参数监听器
     * @example
     * emitter.emit('complete');
     */
    public emit(eventName: string): void;
    
    /**
     * 触发带参数事件
     * @template T - 事件参数的类型
     * @param {string} eventName - 事件名称
     * @param {T} detail - 传递给监听器的事件数据
     * @description 调用该事件的所有带参数监听器，并将detail作为参数传递
     * @example
     * emitter.emit<number>('progress', 75);
     */
    public emit<T = any>(eventName: string, detail: T): void;
    
    /**
     * 事件触发实现
     * @param {string} eventName - 事件名称
     * @param {any} [detail] - 可选的事件数据
     * @description 根据是否提供detail参数，自动选择对应类型的监听器调用方式
     * 每个监听器的异常会被捕获并记录，不会影响其他监听器的执行
     */
    public emit(eventName: string, detail?: any): void {
        if (this.__events[eventName]) {
            this.__events[eventName].forEach(callback => {
                try {
                    // 根据参数存在性决定调用方式
                    if (detail === undefined) {
                        // 调用无参数监听器
                        (callback as () => void)();
                    } else {
                        // 调用带参数监听器
                        (callback as (detail: any) => void)(detail);
                    }
                } catch (error) {
                    console.error(`Event listener error for ${eventName}:`, error);
                }
            });
        }
    }
}

export default EventEmitter;